import { join } from 'path';

/**
 * 规范化文件路径，将Windows反斜杠转换为Unix正斜杠
 */
function normalizePath(filePath: string): string {
  return filePath.replace(/\\/g, '/');
}

interface LcovRecord {
  sourceFile: string;
  lines: { [lineNumber: number]: number };
  functions: { name: string; lineNumber: number; hit: number }[];
  branches: {
    lineNumber: number;
    blockNumber: number;
    branchNumber: number;
    taken: number;
  }[];
  // 汇总数据
  functionsFound?: number;
  functionsHit?: number;
  branchesFound?: number;
  branchesHit?: number;
}

interface CoverageSummary {
  lines: { found: number; hit: number; percent: number };
  functions: { found: number; hit: number; percent: number };
  branches: { found: number; hit: number; percent: number };
}

export class LcovParser {
  static parse(lcovContent: string): LcovRecord[] {
    const records: LcovRecord[] = [];
    const sections = lcovContent
      .split('end_of_record')
      .filter(section => section.trim());

    for (const section of sections) {
      const lines = section.trim().split('\n');
      const record: LcovRecord = {
        sourceFile: '',
        lines: {},
        functions: [],
        branches: [],
        functionsFound: 0,
        functionsHit: 0,
        branchesFound: 0,
        branchesHit: 0,
      };

      for (const line of lines) {
        const [prefix, value] = line.split(':');
        if (!value) {
          continue; // Skip lines without values
        }

        switch (prefix) {
          case 'SF':
            record.sourceFile = normalizePath(value);
            break;
          case 'DA': {
            const [lineNum, hitCount] = value.split(',').map(Number);
            if (lineNum && hitCount !== undefined) {
              record.lines[Number(lineNum)] = hitCount;
            }
            break;
          }
          case 'FN': {
            const [_fnLineNum, _fnName] = value.split(',');
            // Store function info for later (currently unused)
            break;
          }
          case 'FNDA': {
            const [fnHitCount, fnNameHit] = value.split(',');
            if (fnNameHit) {
              record.functions.push({
                name: fnNameHit,
                lineNumber: 0, // Will be set when we have FN data
                hit: Number(fnHitCount),
              });
            }
            break;
          }
          case 'FNF': {
            // Functions found (total)
            record.functionsFound = Number(value);
            break;
          }
          case 'FNH': {
            // Functions hit
            record.functionsHit = Number(value);
            break;
          }
          case 'BDA': {
            const [branchLineNum, branchBlockNum, branchNum, branchTaken] =
              value.split(',').map(Number);
            if (
              branchLineNum &&
              branchBlockNum &&
              branchNum &&
              branchTaken !== undefined
            ) {
              record.branches.push({
                lineNumber: branchLineNum,
                blockNumber: branchBlockNum,
                branchNumber: branchNum,
                taken: branchTaken,
              });
            }
            break;
          }
          case 'BRF': {
            // Branches found (total)
            record.branchesFound = Number(value);
            break;
          }
          case 'BRH': {
            // Branches hit
            record.branchesHit = Number(value);
            break;
          }
        }
      }

      if (record.sourceFile) {
        records.push(record);
      }
    }

    return records;
  }

  static generateSummary(records: LcovRecord[]): CoverageSummary {
    let totalLines = 0;
    let hitLines = 0;
    let totalFunctions = 0;
    let hitFunctions = 0;
    let totalBranches = 0;
    let hitBranches = 0;

    for (const record of records) {
      // 行覆盖率
      const lineEntries = Object.entries(record.lines);
      totalLines += lineEntries.length;
      hitLines += lineEntries.filter(([, count]) => count > 0).length;

      // 函数覆盖率 - 优先使用汇总数据
      if (
        record.functionsFound !== undefined &&
        record.functionsHit !== undefined
      ) {
        totalFunctions += record.functionsFound;
        hitFunctions += record.functionsHit;
      } else {
        // 回退到详细数据
        totalFunctions += record.functions.length;
        hitFunctions += record.functions.filter(fn => fn.hit > 0).length;
      }

      // 分支覆盖率 - 优先使用汇总数据
      if (
        record.branchesFound !== undefined &&
        record.branchesHit !== undefined
      ) {
        totalBranches += record.branchesFound;
        hitBranches += record.branchesHit;
      } else {
        // 回退到详细数据
        totalBranches += record.branches.length;
        hitBranches += record.branches.filter(
          branch => branch.taken > 0
        ).length;
      }
    }

    return {
      lines: {
        found: totalLines,
        hit: hitLines,
        percent: (hitLines / (totalLines || 1)) * 100,
      },
      functions: {
        found: totalFunctions,
        hit: hitFunctions,
        percent: (hitFunctions / (totalFunctions || 1)) * 100,
      },
      branches: {
        found: totalBranches,
        hit: hitBranches,
        percent: totalBranches > 0 ? (hitBranches / totalBranches) * 100 : 0,
      },
    };
  }
}

export class HtmlReportGenerator {
  private static getColorForCoverage(percent: number): string {
    if (percent >= 80) {
      return '#4CAF50';
    }
    if (percent >= 60) {
      return '#FF9800';
    }
    return '#F44336';
  }

  private static generateFileReport(
    record: LcovRecord,
    sourceCode?: string
  ): string {
    const lines = sourceCode ? sourceCode.split('\n') : [];
    let fileHtml = '';

    for (let i = 0; i < lines.length; i++) {
      const lineNumber = i + 1;
      const hitCount = record.lines[Number(lineNumber)];
      const lineClass =
        hitCount === undefined
          ? 'uncovered'
          : hitCount > 0
            ? 'covered'
            : 'missed';
      const hitDisplay = hitCount ?? '';

      fileHtml += `
        <tr class="${lineClass}">
          <td class="line-number">${lineNumber}</td>
          <td class="hit-count">${hitDisplay}</td>
          <td class="source-code"><pre>${this.escapeHtml(lines[Number(i)] ?? '')}</pre></td>
        </tr>
      `;
    }

    return fileHtml;
  }
  private static escapeHtml(text: string): string {
    return text
      .replace(/&/g, '&amp;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/"/g, '&quot;')
      .replace(/'/g, '&#39;');
  }

  static generate(records: LcovRecord[], outputPath: string): void {
    const summary = LcovParser.generateSummary(records);

    const html = `
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>测试覆盖率报告</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        
        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            background-color: #f5f5f5;
            color: #333;
            line-height: 1.6;
        }
        
        .container {
            max-width: 1200px;
            margin: 0 auto;
            padding: 20px;
        }
        
        .header {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 30px;
            border-radius: 10px;
            margin-bottom: 30px;
            text-align: center;
        }
        
        .header h1 {
            font-size: 2.5em;
            margin-bottom: 10px;
        }
        
        .header p {
            font-size: 1.2em;
            opacity: 0.9;
        }
        
        .summary {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
            gap: 20px;
            margin-bottom: 30px;
        }
        
        .metric {
            background: white;
            padding: 25px;
            border-radius: 10px;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
            text-align: center;
            border-left: 5px solid;
        }
        
        .metric.lines { border-left-color: ${this.getColorForCoverage(summary.lines.percent)}; }
        .metric.functions { border-left-color: ${this.getColorForCoverage(summary.functions.percent)}; }
        .metric.branches { border-left-color: ${this.getColorForCoverage(summary.branches.percent)}; }
        
        .metric h3 {
            font-size: 1.2em;
            margin-bottom: 15px;
            color: #666;
            text-transform: uppercase;
            letter-spacing: 1px;
        }
        
        .metric .percentage {
            font-size: 3em;
            font-weight: bold;
            margin-bottom: 10px;
        }
        
        .metric .details {
            color: #888;
            font-size: 0.9em;
        }
        
        .files-table {
            background: white;
            border-radius: 10px;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
            overflow: hidden;
        }
        
        .files-table h2 {
            padding: 20px;
            background: #f8f9fa;
            border-bottom: 1px solid #e9ecef;
            margin: 0;
        }
        
        table {
            width: 100%;
            border-collapse: collapse;
        }
        
        th, td {
            padding: 12px 15px;
            text-align: left;
            border-bottom: 1px solid #e9ecef;
        }
        
        th {
            background: #f8f9fa;
            font-weight: 600;
            color: #666;
        }
        
        .progress-bar {
            width: 100px;
            height: 20px;
            background: #e9ecef;
            border-radius: 10px;
            overflow: hidden;
            position: relative;
        }
        
        .progress-fill {
            height: 100%;
            transition: width 0.3s ease;
        }
        
        .file-name {
            font-family: 'Monaco', 'Menlo', monospace;
            font-size: 0.9em;
        }
        
        .timestamp {
            text-align: center;
            color: #888;
            margin-top: 30px;
            padding-top: 20px;
            border-top: 1px solid #e9ecef;
        }
        
        .legend {
            background: white;
            padding: 20px;
            border-radius: 10px;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
            margin-bottom: 30px;
        }
        
        .legend h3 {
            margin-bottom: 15px;
        }
        
        .legend-item {
            display: inline-block;
            margin-right: 20px;
            margin-bottom: 10px;
        }
        
        .legend-color {
            display: inline-block;
            width: 20px;
            height: 20px;
            border-radius: 3px;
            margin-right: 8px;
            vertical-align: middle;
        }
        
        .excellent { background-color: #4CAF50; }
        .good { background-color: #FF9800; }
        .poor { background-color: #F44336; }
        
        @media (max-width: 768px) {
            .container {
                padding: 10px;
            }
            
            .header h1 {
                font-size: 2em;
            }
            
            .summary {
                grid-template-columns: 1fr;
            }
            
            .metric .percentage {
                font-size: 2.5em;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>🧪 测试覆盖率报告</h1>
            <p>生成时间: ${new Date().toLocaleString('zh-CN')}</p>
        </div>
        
        <div class="legend">
            <h3>📊 覆盖率图例</h3>
            <div class="legend-item">
                <span class="legend-color excellent"></span>
                优秀 (≥80%)
            </div>
            <div class="legend-item">
                <span class="legend-color good"></span>
                良好 (60-79%)
            </div>
            <div class="legend-item">
                <span class="legend-color poor"></span>
                需要改进 (<60%)
            </div>
        </div>
        
        <div class="summary">
            <div class="metric lines">
                <h3>📝 代码行覆盖率</h3>
                <div class="percentage" style="color: ${this.getColorForCoverage(summary.lines.percent)}">
                    ${summary.lines.percent.toFixed(1)}%
                </div>
                <div class="details">
                    ${summary.lines.hit} / ${summary.lines.found} 行
                </div>
            </div>
            
            <div class="metric functions">
                <h3>🔧 函数覆盖率</h3>
                <div class="percentage" style="color: ${this.getColorForCoverage(summary.functions.percent)}">
                    ${summary.functions.percent.toFixed(1)}%
                </div>
                <div class="details">
                    ${summary.functions.hit} / ${summary.functions.found} 函数
                </div>
            </div>
            
            <div class="metric branches">
                <h3>🌿 分支覆盖率</h3>
                <div class="percentage" style="color: ${this.getColorForCoverage(summary.branches.percent)}">
                    ${summary.branches.percent.toFixed(1)}%
                </div>
                <div class="details">
                    ${summary.branches.hit} / ${summary.branches.found} 分支
                </div>
            </div>
        </div>
        
        <div class="files-table">
            <h2>📁 文件覆盖率详情</h2>
            <table>
                <thead>
                    <tr>
                        <th>文件</th>
                        <th>行覆盖率</th>
                        <th>函数覆盖率</th>
                        <th>分支覆盖率</th>
                    </tr>
                </thead>
                <tbody>
                    ${records
                      .map(record => {
                        const lineTotal = Object.keys(record.lines).length;
                        const lineHit = Object.values(record.lines).filter(
                          count => count > 0
                        ).length;
                        const linePercent =
                          lineTotal > 0 ? (lineHit / lineTotal) * 100 : 0;

                        // 函数覆盖率 - 优先使用汇总数据
                        const funcTotal =
                          record.functionsFound ?? record.functions.length;
                        const funcHit =
                          record.functionsHit ??
                          record.functions.filter(fn => fn.hit > 0).length;
                        const funcPercent =
                          funcTotal > 0 ? (funcHit / funcTotal) * 100 : 0;

                        // 分支覆盖率 - 优先使用汇总数据
                        const branchTotal =
                          record.branchesFound ?? record.branches.length;
                        const branchHit =
                          record.branchesHit ??
                          record.branches.filter(branch => branch.taken > 0)
                            .length;
                        const branchPercent =
                          branchTotal > 0 ? (branchHit / branchTotal) * 100 : 0;

                        return `
                        <tr>
                            <td class="file-name">${normalizePath(record.sourceFile)}</td>
                            <td>
                                <div class="progress-bar">
                                    <div class="progress-fill" 
                                         style="width: ${linePercent}%; background-color: ${this.getColorForCoverage(linePercent)}">
                                    </div>
                                </div>
                                <span>${linePercent.toFixed(1)}% (${lineHit}/${lineTotal})</span>
                            </td>
                            <td>
                                <div class="progress-bar">
                                    <div class="progress-fill" 
                                         style="width: ${funcPercent}%; background-color: ${this.getColorForCoverage(funcPercent)}">
                                    </div>
                                </div>
                                <span>${funcPercent.toFixed(1)}% (${funcHit}/${funcTotal})</span>
                            </td>
                            <td>
                                <div class="progress-bar">
                                    <div class="progress-fill" 
                                         style="width: ${branchPercent}%; background-color: ${this.getColorForCoverage(branchPercent)}">
                                    </div>
                                </div>
                                <span>${branchPercent.toFixed(1)}% (${branchHit}/${branchTotal})</span>
                            </td>
                        </tr>
                      `;
                      })
                      .join('')}
                </tbody>
            </table>
        </div>
        
        <div class="timestamp">
            <p>📊 报告由 Bun 测试框架生成 | ⚡ 构建工具: Bun + TypeScript</p>
        </div>
    </div>
    
    <script>
        // 添加进度条动画
        document.addEventListener('DOMContentLoaded', function() {
            const progressBars = document.querySelectorAll('.progress-fill');
            progressBars.forEach(bar => {
                const width = bar.style.width;
                bar.style.width = '0%';
                setTimeout(() => {
                    bar.style.width = width;
                }, 100);
            });
        });
    </script>
</body>
</html>
    `;

    // eslint-disable-next-line security/detect-non-literal-fs-filename
    require('fs').writeFileSync(join(outputPath, 'index.html'), html);
  }
}
